package com.hyron.server.controller;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hyron.server.form.AuthToken;
import com.hyron.server.form.EventInfo;
import com.hyron.server.form.Message;
import com.hyron.server.form.SignInForm;
import com.hyron.server.form.UploadMediaRet;
import com.hyron.server.form.UserModel;
import com.hyron.server.util.CommonUtil;
import com.hyron.server.util.QRCodeEncoderHandler;
import com.hyron.server.util.StringComparator;
import com.mysql.jdbc.StringUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

@Controller 
public class WebchatController{
	
	public static AuthToken authTokenInstance = null;
	
	@Value("${app.token}")
	private String appToken;
	
	@Value("${api.url}")
	private String appURL;
	
	@Value("${api.upload.url}")
	private String apiUploadUrl;
	
	@Value("${appID}")
	private String appID;
	
	@Value("${appsecret}")
	private String appsecret;
	
	@Value("${image.temp.path}")
	private String IMAGE_TEMP_PATH;
	
	private final String AUTH_TOKEN_API = "token?grant_type=client_credential&appid=%s&secret=%s";
	private final String MENU_API = "menu/create?access_token=%s";
	private final String GET_USER_API = "user/info?access_token=%s&openid=%s&lang=zh_CN";
	
	private ObjectMapper objectMapper = new ObjectMapper();
	
	private Logger logger = LogManager.getLogger(getClass());
	
	@Scheduled(cron="0 0 1/1 * * ?")
	public void autoRefreshAuthToken() throws Exception{
		generateAuthToken(true);
		logger.info("auto refresh authToken: " + authTokenInstance.getAccess_token());
	}
	
	@RequestMapping("refreshAuthToken")
	public String refreshAutoToken(ModelMap model) throws Exception{
		model.addAttribute("authToken", generateAuthToken(true));
		return "refreshAuthToken";
	}
	
	@RequestMapping("updateMenu")
	public String updateMenu(String menuJson, ModelMap model) throws Exception, IOException{
		if(!StringUtils.isNullOrEmpty(menuJson)){
			String authToken = generateAuthToken(false);
			HttpClient client = HttpClientBuilder.create().build();
			String url = appURL + String.format(MENU_API, authToken);
			HttpPost post = new HttpPost(url);
			StringEntity se = new StringEntity(menuJson, HTTP.UTF_8); 
			
			post.setEntity(se);
	
	        HttpResponse response = client.execute(post);
	        
	        Message message = objectMapper.readValue(response.getEntity().getContent(), Message.class);
	        model.addAttribute("message", message);
	        model.addAttribute("menuJson", menuJson);
		}
        
		return "updateMenu";
	}
	
	@RequestMapping(value="/", method=RequestMethod.GET)
	public String wechatServerGet(SignInForm signInForm, HttpServletRequest request, HttpServletResponse response, ModelMap model) throws IOException{
		logger.info("Wechat Server Get.");
		
		String signature = signInForm.getSignature();
        String timestamp = signInForm.getTimestamp();
        String nonce = signInForm.getNonce();
        
        logger.info("signature: " + signature + " timestamp: " + timestamp + " nonce: " + nonce + " Echostr: " + signInForm.getEchostr());
        		
        String [] array = new String[]{appToken, timestamp, nonce};
        Arrays.sort(array, new StringComparator(StringComparator.ASC));

        String code = "";
        for(String str : array){
        	code += str;
        }
        
        code = CommonUtil.SHA1(code);
        
        logger.info("code: " + code);
		if( signature != null && signature.indexOf(code) != -1){
			response.getWriter().write(signInForm.getEchostr());
		}else{
			response.getWriter().write("false");
			logger.error("Error occured when try to sign in." + signInForm.getEchostr());
			System.out.println("Error occured when try to sign in." + signInForm.getEchostr());
		}
		
		return null;
	}
	
	public static void main(String[] args){
		SignInForm signInForm = new SignInForm();
		signInForm.setSignature("9ebc478b4f732f90ef550134b459bd63f2af6c49");
		signInForm.setNonce("1072721517");
		signInForm.setTimestamp("1402640935");
		try {
			WebchatController c = new WebchatController();
			c.wechatServerGet(signInForm, null, null, null);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@RequestMapping(value="/", method=RequestMethod.POST)
	public String wechatServerPost(SignInForm signInForm, HttpServletRequest request, HttpServletResponse response, ModelMap model) throws Exception{
		logger.error("Wechat Server Post.");
		
		XStream stream=new XStream(new DomDriver());
		stream.alias("xml", EventInfo.class);
		stream.aliasField("ToUserName", EventInfo.class, "toUserName");
		stream.aliasField("FromUserName", EventInfo.class, "fromUserName");
		stream.aliasField("CreateTime", EventInfo.class, "createTime");
		stream.aliasField("MsgType", EventInfo.class, "msgType");
		stream.aliasField("Event", EventInfo.class, "event");
		stream.aliasField("EventKey", EventInfo.class, "eventKey");
		
		String content = CommonUtil.inputStream2String(request.getInputStream());
		logger.info("Posted Content from server: " + content);
		System.out.println(content);
		EventInfo eventInfo = (EventInfo) stream.fromXML(content);
		
		if(eventInfo != null && !StringUtils.isNullOrEmpty(eventInfo.getFromUserName())){
			UserModel userModel = loadUserbyId(eventInfo.getFromUserName());
			if(userModel != null){
				String imagePath = generateBarCode(userModel);
				String mediaId = postBarCodeToWechat(imagePath);
				
				String xml = generateResponseXML(eventInfo, mediaId);
				if(!StringUtils.isNullOrEmpty(xml)){
					logger.info("response xml: " + xml);
					response.getWriter().write(xml);
				}else{
					logger.error("generate xml error");
				}
			}else{
				logger.error("Load user info error");
			}
		}else{
			logger.error("Parse event info error");
		}
		
		return null;
	}
	
	private String generateBarCode(UserModel userModel){
		String content = stringfyUser(userModel);
		String imagePath = IMAGE_TEMP_PATH + UUID.randomUUID().toString() + ".png";
		QRCodeEncoderHandler handler = new QRCodeEncoderHandler(); 
        handler.encoderQRCode(content, imagePath); 
		
        return imagePath;
	}
	
	private String stringfyUser(UserModel user){
		//QRcode max length is 120
		String sex = ("1".equals(user.getSex())) ? "男" : "女";
		return "nickname: " + user.getNickname() + "\n" +
				"sex: " + sex + "\n" +
				"city: " + user.getCity() + "\n" +
				"province: " + user.getProvince() + "\n" + 
				"country: " + user.getCountry() + "\n";
				//"headimgurl: " + user.getHeadimgurl();
	}
	
	private String generateResponseXML(EventInfo eventInfo, String mediaId){
		if(eventInfo == null || StringUtils.isNullOrEmpty(mediaId)){
			return null;
		}
		String xml = "<xml>" +
		"<ToUserName><![CDATA[" + eventInfo.getFromUserName() + "]]></ToUserName>" +
		"<FromUserName><![CDATA[" + eventInfo.getToUserName() + "]]></FromUserName>" +
		"<CreateTime>" + eventInfo.getCreateTime() + "</CreateTime>" +
		"<MsgType><![CDATA[image]]></MsgType>" +
		"<Image>" +
		"<MediaId><![CDATA[" + mediaId + "]]></MediaId>" +
		"</Image>" +
		"</xml>";
		
		return xml;
	}
	
	/**
	 * 
	 * @return Media Id
	 * @throws Exception 
	 * @throws ClientProtocolException 
	 */
	private String postBarCodeToWechat(String imagePath) throws ClientProtocolException, Exception{
		
        String url = String.format(apiUploadUrl, generateAuthToken(false), "image");
        HttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost httppost = new HttpPost(url);
        
        FileBody bin = new FileBody(new File(imagePath));

        HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("media", bin).build();
        httppost.setEntity(reqEntity);

        System.out.println("executing request " + httppost.getRequestLine());
        HttpResponse response = httpclient.execute(httppost);
        
        if(HttpStatus.SC_OK == response.getStatusLine().getStatusCode()){
        	String content = CommonUtil.inputStream2String(response.getEntity().getContent());
			logger.info("upload image: " + content);
        	UploadMediaRet uploadMediaRet = objectMapper.readValue(content, UploadMediaRet.class);
        	return uploadMediaRet.getMedia_id();
        }else{
			logger.error("upload image to server error");
			return null;
		}
	}
	
	private UserModel loadUserbyId(String id) throws Exception{
		HttpClient client = HttpClientBuilder.create().build();
		String url = appURL + String.format(GET_USER_API, generateAuthToken(false), id);
		HttpGet get = new HttpGet(url);
		
		HttpResponse response = client.execute(get);
		
		if(HttpStatus.SC_OK == response.getStatusLine().getStatusCode()){
			String content = CommonUtil.inputStream2String(response.getEntity().getContent());
			logger.info("load user: " + content);
			UserModel userModel = objectMapper.readValue(content, UserModel.class);
			return userModel;
		}else{
			logger.error("load user by " + id + " error");
			return null;
		}
	}
	
	private String generateAuthToken(boolean isRefresh) throws Exception{
		if(isRefresh || authTokenInstance == null){
			HttpClient client = HttpClientBuilder.create().build();
			String url = appURL + String.format(AUTH_TOKEN_API, appID, appsecret);
			HttpGet get = new HttpGet(url);
			
			HttpResponse response = client.execute(get);
			
			authTokenInstance = objectMapper.readValue(response.getEntity().getContent(), AuthToken.class);
			/*authTokenInstance = new AuthToken();
			authTokenInstance.setAccess_token("8LQ4TIkW1Mk_TU8l6VA_GdXLqfCPFQ8O2QB5mIDHJsLYjQRUcCTLYwlhjI7moMj0akeO1fZde3v7RUAUZmAmRg");
			authTokenInstance.setExpires_in("7200");*/
		}
		return authTokenInstance.getAccess_token();
	}
}
